package com.wanghua.mall.ware.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wanghua.common.dto.mq.OrderDto;
import com.wanghua.common.dto.mq.StockDetailDto;
import com.wanghua.common.dto.mq.StockLockedDto;
import com.wanghua.common.utils.PageUtils;
import com.wanghua.common.utils.Query;
import com.wanghua.common.utils.R;
import com.wanghua.mall.ware.config.WareRabbitMQConfig;
import com.wanghua.mall.ware.dao.WareSkuDao;
import com.wanghua.mall.ware.entity.WareOrderTaskDetailEntity;
import com.wanghua.mall.ware.entity.WareOrderTaskEntity;
import com.wanghua.mall.ware.entity.WareSkuEntity;
import com.wanghua.mall.ware.enums.WareStockLockedEnum;
import com.wanghua.mall.ware.exception.NoStockException;
import com.wanghua.mall.ware.openfeign.OrderClient;
import com.wanghua.mall.ware.openfeign.ProductSkuInfoClient;
import com.wanghua.mall.ware.service.WareOrderTaskDetailService;
import com.wanghua.mall.ware.service.WareOrderTaskService;
import com.wanghua.mall.ware.service.WareSkuService;
import com.wanghua.mall.ware.vo.OrderItemVo;
import com.wanghua.mall.ware.vo.WareSkuStockVo;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;


@Service
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {

    @Resource
    private ProductSkuInfoClient productSkuInfoClient;
    @Resource
    private WareOrderTaskService wareOrderTaskService;
    @Resource
    private WareOrderTaskDetailService wareOrderTaskDetailService;
    @Resource
    private RabbitTemplate rabbitTemplate;
    @Resource
    private OrderClient orderClient;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        QueryWrapper<WareSkuEntity> queryWrapper = new QueryWrapper<>();
        String wareId = (String) params.get("wareId");
        if (StringUtils.hasLength(wareId)) {
            queryWrapper.eq("ware_id", wareId);
        }
        String skuId = (String) params.get("skuId");
        if (StringUtils.hasLength(skuId)) {
            queryWrapper.eq("sku_id", skuId);
        }

        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params),
                queryWrapper
        );

        return new PageUtils(page);
    }

    //3.将成功采购的物品进行入库
    @Transactional
    @Override
    public void addStock(Long skuId, Long wareId, Integer skuNum) {
        List<WareSkuEntity> wareSkuEntityList = baseMapper.selectList(new QueryWrapper<WareSkuEntity>().eq("sku_id", skuId).eq("ware_id", wareId));
        if (wareSkuEntityList == null || wareSkuEntityList.size() == 0) {
            //新增
            WareSkuEntity wareSkuEntity = new WareSkuEntity();
            wareSkuEntity.setSkuId(skuId);
            wareSkuEntity.setWareId(wareId);
            wareSkuEntity.setStock(skuNum);
            //远程查询商品名称,失败后整个事务不用回滚
            try {
                R r = productSkuInfoClient.info(skuId);
                if (r.getCode() == 0) {
                    Map<String, Object> map = (Map<String, Object>) r.get("skuInfo");
                    wareSkuEntity.setSkuName((String) map.get("skuName"));
                }
            } catch (Exception ignored) {
            }
            wareSkuEntity.setStockLocked(0);
            baseMapper.insert(wareSkuEntity);
        } else {
            //更新
            this.updateBatchById(wareSkuEntityList.stream().map((wareSkuEntity) -> {
                wareSkuEntity.setStock(wareSkuEntity.getStock() + skuNum);
                return wareSkuEntity;
            }).collect(Collectors.toList()));
        }
    }

    //查询sku是否有库存
    @Override
    public Map<Long, Boolean> getStockBySku(List<Long> skuIdList) {
        HashMap<Long, Boolean> map = new HashMap<>();
        skuIdList.forEach((skuId) -> {
            //查询sku的总库存
            Integer stock = baseMapper.getAllStock(skuId);
            map.put(skuId, stock != null && stock > 0);
        });
        System.out.println(map);
        return map;
    }

    /**
     * 为订单锁定库存
     * 默认运行时异常都会回滚
     * (rollbackFor = {NoStockException.class})
     *
     * @param wareSkuStockVo
     * @return
     */
    @Transactional
    @Override
    public Boolean orderLockStock(WareSkuStockVo wareSkuStockVo) {
        //保存库存工作单的详情
        WareOrderTaskEntity wareOrderTaskEntity = new WareOrderTaskEntity();
        wareOrderTaskEntity.setOrderNo(wareSkuStockVo.getOrderNo());
        wareOrderTaskService.save(wareOrderTaskEntity);

        //按照下单的收货地址，选择就近的仓库锁定库存，不做
        List<OrderItemVo> orderItemVoList = wareSkuStockVo.getOrderItemVoList();
        //Map<Long, List<Long>>  sku在哪些仓库有库存
        Map<Long, List<Long>> skuWareHasStockMap = orderItemVoList.stream().collect(Collectors.toMap(OrderItemVo::getSkuId, (orderItemVo) -> baseMapper.listWareIdHasStockBySkuId(orderItemVo.getSkuId())));
        //Map<Long, Integer> 要锁定的sku的商品数量
        Map<Long, Integer> skuCountMap = orderItemVoList.stream().collect(Collectors.toMap(OrderItemVo::getSkuId, OrderItemVo::getCount));

        //锁定库存
        boolean allLocked = true;//标志所有sku是都被锁定
        for (Long skuId : skuWareHasStockMap.keySet()) {
            List<Long> wareIdList = skuWareHasStockMap.get(skuId);
            boolean skuStocked = false;//标志该sku是否被锁定库存
            if (wareIdList == null || wareIdList.size() == 0) {
                throw new NoStockException(skuId);
            }
            for (Long wareId : wareIdList) {
                //数据库锁定库存操作,返回1成功，返回0失败
                Long count = baseMapper.lockSkuStock(skuId, wareId, skuCountMap.get(skuId));
                if (count == 1) {//锁定成功
                    skuStocked = true;
                    //告诉RabbitMQ库存锁定成功
                    WareOrderTaskDetailEntity wareOrderTaskDetailEntity = new WareOrderTaskDetailEntity(
                            null, skuId, null, skuCountMap.get(skuId), wareOrderTaskEntity.getId(),
                            wareId, WareStockLockedEnum.LOCKED.getCode(), null, null);
                    wareOrderTaskDetailService.save(wareOrderTaskDetailEntity);
                    StockDetailDto stockDetailDto = new StockDetailDto();
                    BeanUtils.copyProperties(wareOrderTaskDetailEntity, stockDetailDto);
                    StockLockedDto stockLockedDto = new StockLockedDto(wareOrderTaskEntity.getId(), stockDetailDto);
                    //要发送的消息不能只包含工作单id
                    rabbitTemplate.convertAndSend(
                            WareRabbitMQConfig.STOCK_EVENT_EXCHANGE_NAME,
                            WareRabbitMQConfig.STOCK_LOCKED_BINDING_NAME,
                            stockLockedDto
                    );
                    break;
                }
            }
            if (!skuStocked) {//该sku未被锁定库存
                allLocked = false;
                throw new NoStockException(skuId);
            }
        }

        return allLocked;
    }

    /**
     * 1、库存自动解锁
     * 下订单成功，库存锁定成功，接下来的业务调用失败，导致订单回滚。之前锁定的库存就要自动解锁
     * <p>
     * <p>
     * 2、订单失败
     * 库存锁定失败
     * <p>
     * 只要解锁库存的消息失败，一定要告诉服务解锁失败
     *
     * @param stockLockedDto stockLockedDto
     */
    @Override
    public boolean releaseLockStock(StockLockedDto stockLockedDto) {
        Long taskId = stockLockedDto.getTaskId();
        StockDetailDto stockDetailDto = stockLockedDto.getStockDetailDto();

        WareOrderTaskDetailEntity wareOrderTaskDetailEntity = wareOrderTaskDetailService.getById(stockDetailDto.getId());
        if (wareOrderTaskDetailEntity != null) { //需要解锁
            /**
             * 解锁
             * 1、查询数据库关于这个订单锁定库存信息
             *   有：证明库存锁定成功了
             *      解锁：订单状况
             *          1、没有这个订单，必须解锁库存
             *          2、有这个订单，不一定解锁库存
             *              订单状态：已取消：解锁库存
             *                      已支付：不能解锁库存
             */
            WareOrderTaskEntity wareOrderTaskEntity = wareOrderTaskService.getById(taskId);
            String orderNo = wareOrderTaskEntity.getOrderNo();//订单号
            R r = orderClient.getOrderStatus(orderNo);
            if (r.getCode() == 0) {//成功
                Integer orderStatus = (Integer) r.get("data");
                if (orderStatus == 4) {//订单状态：已取消：解锁库存
                    baseMapper.releaseLockStock(stockDetailDto.getSkuId(), stockDetailDto.getWareId(), stockDetailDto.getSkuNum());
                    // TODO 解锁后更新订单状态和其他相关表的状态
                    return true;
                }

            } else {
                return false;
            }
        }
        return true;
    }

    /**
     * 订单关闭，准备解锁库存
     *
     * @param orderDto
     */
    @Override
    public void releaseLockStock(OrderDto orderDto) {
        String orderNo = orderDto.getOrderNo();

        //查询最新库存状态，防止重复解锁库存
        WareOrderTaskEntity wareOrderTaskEntity = wareOrderTaskService.getTaskByOrderNo(orderNo);
        //按照库存工作单找到所有没有进行解锁的库存进行解锁
        List<WareOrderTaskDetailEntity> wareOrderTaskDetailEntityList = wareOrderTaskDetailService.
                list(new QueryWrapper<WareOrderTaskDetailEntity>()
                        .eq("task_id", wareOrderTaskEntity.getId())
                        .eq("lock_status", WareStockLockedEnum.LOCKED.getCode()));
        for (WareOrderTaskDetailEntity wareOrderTaskDetailEntity : wareOrderTaskDetailEntityList) {
            baseMapper.releaseLockStock(wareOrderTaskDetailEntity.getSkuId(),
                    wareOrderTaskDetailEntity.getWareId(),
                    wareOrderTaskDetailEntity.getSkuNum());
        }

    }


}